import {Component, Input, OnInit} from '@angular/core';
import {UtilComponent} from '../../interface/util-component.interface';
import Map from 'ol/Map.js';
import View from 'ol/View.js';
import {getWidth, getCenter} from 'ol/extent.js';
import WMTSCapabilities from 'ol/format/WMTSCapabilities.js';
import TileLayer from 'ol/layer/Tile.js';
import {get as getProjection} from 'ol/proj.js';
import {register} from 'ol/proj/proj4.js';
import {OSM, TileImage, TileWMS, XYZ} from 'ol/source.js';
import WMTS, {optionsFromCapabilities} from 'ol/source/WMTS.js';
import TileGrid from 'ol/tilegrid/TileGrid.js';
import proj4 from 'proj4';

@Component({
    selector: 'kylin-raster-reprojection',
    templateUrl: './raster-reprojection.component.html',
    styleUrls: ['./raster-reprojection.component.less']
})
export class RasterReprojectionComponent implements OnInit, UtilComponent {
// 光栅投影
    hasContent = true;
    map: Map;

    @Input() set data(val) {
        Object.assign(this, val);
    };

    baseLayerOptions = [{label: 'OSM (EPSG:3857)', value: 'osm'}, {label: 'WMS (EPSG:3857)', value: 'wms4326'}];
    overLayerOptions = [
        /* 该地图数据存在跨域问题，注释掉
        {label: 'British National Grid (EPSG:27700)', value: 'bng'},*/
        {label: 'Swisstopo WMS (EPSG:21781)', value: 'wms21781'},
        {label: 'NASA Arctic WMTS (EPSG:3413)', value: 'wmts3413'},
        {label: 'Grand Canyon HiDPI (EPSG:3857)', value: 'grandcanyon'},
        {label: 'United States (EPSG:3857)', value: 'states'}
    ];
    viewOptions = [
        {label: 'Spherical Mercator (EPSG:3857)', value: 'EPSG:3857'},
        {label: 'WGS 84 (EPSG:3857)', value: 'EPSG:3857'},
        {label: 'Mollweide (ESRI:54009)', value: 'ESRI:54009'},
        {label: 'British National Grid (EPSG:27700)', value: 'EPSG:27700'},
        {label: 'ED50 / UTM zone 32N (EPSG:23032)', value: 'EPSG:23032'},
        {label: 'US National Atlas Equal Area (EPSG:2163)', value: 'EPSG:2163'},
        {label: 'NSIDC Polar Stereographic North (EPSG:3413)', value: 'EPSG:3413'},
        {label: 'RSRGD2000 / MSLC2000 (EPSG:5479)', value: 'EPSG:5479'}
    ];

    proj27700: any;
    proj23032: any;
    proj5479: any;
    proj21781: any;
    proj3413: any;
    proj2163: any;
    proj54009: any;

    layers = {};
    renderEdges = false;

    baseLayer = 'osm';
    overlayLayer = 'wmts3413';
    viewProj = 'EPSG:3857';
    renderEdgesChecked: any;
    originView: any;
    originLayerLen: number; // 记录打开重投影功能前的layer个数，以在后续操作中跳过这些layers

    constructor() {
        this.registerProj4();
        this.proj27700 = getProjection('EPSG:27700');
        this.proj27700.setExtent([0, 0, 700000, 1300000]);

        this.proj23032 = getProjection('EPSG:23032');
        this.proj23032.setExtent([-1206118.71, 4021309.92, 1295389.00, 8051813.28]);

        this.proj5479 = getProjection('EPSG:5479');
        this.proj5479.setExtent([6825737.53, 4189159.80, 9633741.96, 5782472.71]);

        this.proj21781 = getProjection('EPSG:21781');
        this.proj21781.setExtent([485071.54, 75346.36, 828515.78, 299941.84]);

        this.proj3413 = getProjection('EPSG:3413');
        this.proj3413.setExtent([-4194304, -4194304, 4194304, 4194304]);

        this.proj2163 = getProjection('EPSG:2163');
        this.proj2163.setExtent([-8040784.5135, -2577524.9210, 3668901.4484, 4785105.1096]);

        this.proj54009 = getProjection('ESRI:54009');
        this.proj54009.setExtent([-18e6, -9e6, 18e6, 9e6]);
    }

    ngOnInit() {
        this.originLayerLen = this.map.getLayers().getLength();
        const originView = this.map.getView();
        this.originView = {
            center: originView.getCenter(),
            zoom: originView.getZoom(),
            proj: originView.getProjection()
        };
        originView.setCenter([0, 0]);
        originView.setZoom(2);
        this.turnOn();
    }

    turnOn() {
        /* 该地图数据存在跨域问题，注释掉
        this.layers['bng'] = new TileLayer({
            source: new XYZ({
                projection: 'EPSG:27700',
                url: 'https://tileserver.maptiler.com/miniscale/{z}/{x}/{y}.png',
                crossOrigin: '',
                maxZoom: 6
            })
        });*/

        this.layers['osm'] = new TileLayer({
            source: new OSM()
        });

        this.layers['wms4326'] = new TileLayer({
            source: new TileWMS({
                url: 'https://ahocevar.com/geoserver/wms',
                crossOrigin: '',
                params: {
                    'LAYERS': 'ne:NE1_HR_LC_SR_W_DR',
                    'TILED': true
                },
                projection: 'EPSG:3857'
            })
        });

        this.layers['wms21781'] = new TileLayer({
            source: new TileWMS({
                attributions: '© <a href="http://www.geo.admin.ch/internet/geoportal/' +
                    'en/home.html">Pixelmap 1:1000000 / geo.admin.ch</a>',
                crossOrigin: 'anonymous',
                params: {
                    'LAYERS': 'ch.swisstopo.pixelkarte-farbe-pk1000.noscale',
                    'FORMAT': 'image/jpeg'
                },
                url: 'https://wms.geo.admin.ch/',
                projection: 'EPSG:21781'
            })
        });

        const parser = new WMTSCapabilities();
        const url = 'https://map1.vis.earthdata.nasa.gov/wmts-arctic/wmts.cgi?SERVICE=WMTS&request=GetCapabilities';
        fetch(url).then((response) => {
            return response.text();
        }).then((text) => {
            const result = parser.read(text);
            const options = optionsFromCapabilities(result, {
                layer: 'OSM_Land_Mask',
                matrixSet: 'EPSG3413_250m'
            });
            options.crossOrigin = '';
            options.projection = 'EPSG:3413';
            options.wrapX = false;
            this.layers['wmts3413'] = new TileLayer({
                source: new WMTS(/** @type {!module:ol/source/WMTS~Options} */ (options))
            });
        });

        /*this.layers['grandcanyon'] = new TileLayer({
            source: new XYZ({
                url: 'https://tileserver.maptiler.com/grandcanyon@2x/{z}/{x}/{y}.png',
                crossOrigin: '',
                tilePixelRatio: 2,
                maxZoom: 15,
                attributions: 'Tiles © USGS, rendered with ' +
                    '<a href="http://www.maptiler.com/">MapTiler</a>'
            })
        });*/

        const startResolution = getWidth(getProjection('EPSG:3857').getExtent()) / 256;
        const resolutions = new Array(22);
        for (let i = 0, ii = resolutions.length; i < ii; ++i) {
            resolutions[i] = startResolution / Math.pow(2, i);
        }

        this.layers['states'] = new TileLayer({
            source: new TileWMS({
                url: 'https://ahocevar.com/geoserver/wms',
                crossOrigin: '',
                params: {'LAYERS': 'topp:states'},
                serverType: 'geoserver',
                tileGrid: new TileGrid({
                    extent: [-13884991, 2870341, -7455066, 6338219],
                    resolutions: resolutions,
                    tileSize: [512, 256]
                }),
                projection: 'EPSG:3857'
            })
        });

        this.map.addLayer(this.layers['osm']);
        this.map.addLayer(this.layers['wms21781']);

        /*this.map = new Map({
            layers: [
                this.layers['osm'],
                this.layers['bng']
            ],
            target: 'map',
            view: new View({
                projection: 'EPSG:3857',
                center: [0, 0],
                zoom: 2
            })
        });*/

        this.updateViewProjection();
    }

    turnOff() {
        for (const i = this.originLayerLen; i < this.map.getLayers().getLength();) {
            this.map.removeLayer(this.map.getLayers().item(i));
        }
        this.map.getView().setCenter(this.originView.center);
        this.map.getView().setZoom(this.originView.zoom);
        const newView = new View({
            projection: this.originView.proj,
            center: this.originView.center,
            zoom: this.originView.zoom,
        });
        this.map.setView(newView);
    }

    cancel(options?: any): any {
        this.turnOff();
    }

    handleViewProjChange() {
        this.updateViewProjection();
    }

    handleEdgesChange() {
        this.renderEdges = this.renderEdgesChecked;
        this.map.getLayers().forEach((layer, index) => {
            if (index > this.originLayerLen - 1) {
                this.updateRenderEdgesOnLayer(layer);
            }
        });
    };

    updateViewProjection = () => {
        const newProj = getProjection(this.viewProj);
        const newProjExtent = newProj.getExtent();
        const newView = new View({
            projection: newProj,
            center: getCenter(newProjExtent || [0, 0, 0, 0]),
            zoom: 0,
            extent: newProjExtent || undefined
        });
        this.map.setView(newView);

        // Example how to prevent double occurrence of map by limiting layer extent
        /*if (newProj === getProjection('EPSG:3857')) {
            this.layers['bng'].setExtent([-1057216, 6405988, 404315, 8759696]);
        } else {
            this.layers['bng'].setExtent(undefined);
        }*/
    }

    updateRenderEdgesOnLayer = (layer) => {
        if (layer instanceof TileLayer) {
            const source = layer.getSource();
            if (source instanceof TileImage) {
                source.setRenderReprojectionEdges(this.renderEdges);
            }
        }
    };

    setLayer(layerValue: string, type: string) {
        const layer = this.layers[layerValue];
        if (layer) {
            layer.setOpacity(type === 'base' ? 1 : 0.7);
            this.updateRenderEdgesOnLayer(layer);
            this.map.getLayers().setAt(1, layer);
        }
    }

    registerProj4() {
        proj4.defs('EPSG:27700', '+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 ' +
            '+x_0=400000 +y_0=-100000 +ellps=airy ' +
            '+towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 ' +
            '+units=m +no_defs');
        proj4.defs('EPSG:23032', '+proj=utm +zone=32 +ellps=intl ' +
            '+towgs84=-87,-98,-121,0,0,0,0 +units=m +no_defs');
        proj4.defs('EPSG:5479', '+proj=lcc +lat_1=-76.66666666666667 +lat_2=' +
            '-79.33333333333333 +lat_0=-78 +lon_0=163 +x_0=7000000 +y_0=5000000 ' +
            '+ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs');
        proj4.defs('EPSG:21781', '+proj=somerc +lat_0=46.95240555555556 ' +
            '+lon_0=7.439583333333333 +k_0=1 +x_0=600000 +y_0=200000 +ellps=bessel ' +
            '+towgs84=674.4,15.1,405.3,0,0,0,0 +units=m +no_defs');
        proj4.defs('EPSG:3413', '+proj=stere +lat_0=90 +lat_ts=70 +lon_0=-45 +k=1 ' +
            '+x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs');
        proj4.defs('EPSG:2163', '+proj=laea +lat_0=45 +lon_0=-100 +x_0=0 +y_0=0 ' +
            '+a=6370997 +b=6370997 +units=m +no_defs');
        proj4.defs('ESRI:54009', '+proj=moll +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 ' +
            '+units=m +no_defs');
        register(proj4);
    }
}
